/*
 * Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
 *
 * Based on former do_div() implementation from asm-parisc/div64.h:
 *  Copyright (C) 1999 Hewlett-Packard Co
 *  Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
 *
 *
 * Generic C version of 64bit/32bit division and modulo, with
 * 64bit result and 32bit remainder.
 *
 * The fast case for (n>>32 == 0) is handled inline by do_div(). 
 *
 * Code generated for this function might be very inefficient
 * for some CPUs. __div64_32() can be overridden by linking arch-specific
 * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S.
 */

#include <types.h>
#include <math64.h>
#include <util.h>
#include <bitops.h>

uint32_t __div64_32(uint64_t *n, uint32_t base)
{
    uint64_t rem = *n;
    uint64_t b = base;
    uint64_t res, d = 1;
    uint32_t high = rem >> 32;

    /* Reduce the thing a bit first */
    res = 0;
    if (high >= base) {
        high /= base;
        res = (uint64_t) high << 32;
        rem -= (uint64_t) (high*base) << 32;
    }

    while ((int64_t)b > 0 && b < rem) {
        b = b+b;
        d = d+d;
    }

    do {
        if (rem >= b) {
            rem -= b;
            res += d;
        }
        b >>= 1;
        d >>= 1;
    } while (d);

    *n = res;
    return rem;
}

s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
{
    u64 quotient;

    if (dividend < 0) {
        quotient = div_u64_rem(-dividend, abs(divisor), (u32 *)remainder);
        *remainder = -*remainder;
        if (divisor > 0)
            quotient = -quotient;
    } else {
        quotient = div_u64_rem(dividend, abs(divisor), (u32 *)remainder);
        if (divisor < 0)
            quotient = -quotient;
    }
    return quotient;
}

/**
 * div64_u64 - unsigned 64bit divide with 64bit divisor
 * @dividend:   64bit dividend
 * @divisor:    64bit divisor
 *
 * This implementation is a modified version of the algorithm proposed
 * by the book 'Hacker's Delight'.  The original source and full proof
 * can be found here and is available for use without restriction.
 *
 * 'http://www.hackersdelight.org/HDcode/newCode/divDouble.c'
 */
u64 div64_u64(u64 dividend, u64 divisor)
{
    u32 high = divisor >> 32;
    u64 quot;

    if (high == 0) {
        quot = div_u64(dividend, divisor);
    } else {
        int n = 1 + fls(high);
        quot = div_u64(dividend >> n, divisor >> n);

        if (quot != 0)
            quot--;
        if ((dividend - quot * divisor) >= divisor)
            quot++;
    }

    return quot;
}

/**
 * div64_s64 - signed 64bit divide with 64bit divisor
 * @dividend:   64bit dividend
 * @divisor:    64bit divisor
 */
s64 div64_s64(s64 dividend, s64 divisor)
{
    s64 quot, t;

    quot = div64_u64(abs64(dividend), abs64(divisor));
    t = (dividend ^ divisor) >> 63;

    return (quot ^ t) - t;
}

